/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.controller.project;

import cn.ac.nercis.pes.common.constant.project.SecurityLevel;
import cn.ac.nercis.pes.common.utils.ConvertUtils;
import cn.ac.nercis.pes.community.component.cells.NumberTableCell;
import cn.ac.nercis.pes.community.component.cells.opreate.SingleOperateTableCell;
import cn.ac.nercis.pes.community.component.controls.MenuListCell;
import cn.ac.nercis.pes.community.component.controls.PesBlockButton;
import cn.ac.nercis.pes.community.component.dialog.*;
import cn.ac.nercis.pes.community.controller.base.InitDataLoad;
import cn.ac.nercis.pes.community.controller.manage.AddTestedUnitController;
import cn.ac.nercis.pes.community.controller.manage.EvaluationUnitController;
import cn.ac.nercis.pes.community.model.vo.MenuVO;
import cn.ac.nercis.pes.community.model.vo.TableDataModel;
import cn.ac.nercis.pes.community.model.vo.project.CurrentSystemVO;
import cn.ac.nercis.pes.community.service.SysConfigService;
import cn.ac.nercis.pes.community.support.FXMLController;
import cn.ac.nercis.pes.community.support.controls.JFXPopup;
import cn.ac.nercis.pes.community.utils.ControlUtils;
import cn.ac.nercis.pes.community.utils.*;
import cn.ac.nercis.pes.community.views.manage.AddTestedUnitView;
import cn.ac.nercis.pes.community.views.manage.EvaluationUnitView;
import cn.ac.nercis.pes.community.views.project.AddProjectView;
import cn.ac.nercis.pes.community.views.project.AddSystemView;
import cn.ac.nercis.pes.community.views.project.SystemImportView;
import cn.ac.nercis.pes.model.crypto.CryptoType;
import cn.ac.nercis.pes.model.dto.project.ProjectDTO;
import cn.ac.nercis.pes.model.dto.project.SystemDTO;
import cn.ac.nercis.pes.service.project.ProjectService;
import cn.ac.nercis.pes.service.project.SystemExportService;
import cn.ac.nercis.pes.service.project.SystemImportService;
import cn.ac.nercis.pes.service.project.SystemService;
import javafx.application.Platform;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import java.io.*;
import java.net.URL;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;

/**
 * 项目任务管理控制器
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@FXMLController
public class TaskManageController implements Initializable, InitDataLoad {
    private static final String PROJECT_NAME_FORMAT = "[%s]%s";
    private static final String EMPTY_PROJECT_DESC = "当前无项目信息，请先进行添加！";
    private static final String EMPTY_SEARCH_PROJECT_DESC = "无匹配项目信息！";
    @FXML
    private StackPane page;
    @FXML
    private Label projectTitleLbl;
    @FXML
    private Label systemTitleLbl;
    @FXML
    private TextField projectQueryText;
    @FXML
    private Button projectSearchButton;
    @FXML
    private ListView<MenuVO> projectListView;
    @FXML
    private Button addProjectBtn;
    @FXML
    private Button addSystemBtn;
    @FXML
    private VBox taskPane;
    @FXML
    private VBox emptyPane;
    @FXML
    private ImageView emptyImageView;
    @FXML
    private Label emptyDescLbl;

    @FXML
    private Pagination pagination;
    @FXML
    private TableView<SystemDTO> tableView;
    @FXML
    private TableColumn<SystemDTO, String> numberColumn;
    @FXML
    private TableColumn<SystemDTO, String> nameColumn;
    @FXML
    private TableColumn<SystemDTO, String> systemLevelColumn;

    @FXML
    private TableColumn<SystemDTO, String> systemOperateColumn;

    private final ProjectService projectService;
    private final SystemService systemService;


    private final SystemExportService systemExportService;

    private final SysConfigService sysConfigService;

    private final SystemImportService systemImportService;

    private final TableDataModel<SystemDTO> dataModel = new TableDataModel<>();

    private List<ProjectDTO> projectList;
    private ProjectDTO currentProject;
    private JFXPopup expandPopup;

    @Autowired
    public TaskManageController(SystemService systemService,
                                ProjectService projectService,
                                SystemExportService systemExportService,
                                SysConfigService sysConfigService,
                                SystemImportService systemImportService) {
        this.systemService = systemService;
        this.projectService = projectService;
        this.systemExportService = systemExportService;
        this.sysConfigService = sysConfigService;
        this.systemImportService = systemImportService;
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        projectTitleLbl.setGraphic(IconUtils.getGlyph("project-icon","title-icon"));
        systemTitleLbl.setGraphic(IconUtils.getGlyph("system-icon","title-icon"));
        projectSearchButton.setGraphic(IconUtils.getGlyph("search-icon","btn-icon"));
        addProjectBtn.setGraphic(IconUtils.getGlyph("add-project-icon","normal-btn-icon"));
        addSystemBtn.setGraphic(IconUtils.getGlyph("block-add-icon","normal-btn-icon"));

        ControlUtils.initEmptyImageView(this.emptyImageView,log);
        this.initMenuControls();
        this.initSystemTable();

        initializeData();
    }

    @Override
    public void initializeData() {
        this.loadProjectList();
    }

    @FXML
    protected void onProjectSearch(){
        loadProjectList();
    }

    @FXML
    public void onAddProject(){
        GuiStore.setMainBodyContent(SpringUtils.getBean(AddProjectView.class).getView());
        SpringUtils.getBean(AddProjectController.class).initializeData();
    }

    public void onEditProject(){
        var pageDialog = PageDialog.builder()
                .withIcon("dialog-edit-icon")
                .withTitle("项目信息")
                .withShowActions(false)
                .withContent(SpringUtils.getBean(AddProjectView.class).getView())
                .build();
        SpringUtils.getBean(AddProjectController.class).initializeData(this.currentProject);
        pageDialog.show();
    }

    public void onEditUnitItem() {
        var pageDialog = PageDialog.builder()
                .withIcon("dialog-edit-icon")
                .withTitle("测评单位")
                .withShowActions(false)
                .withContent(SpringUtils.getBean(EvaluationUnitView.class).getView())
                .build();
        SpringUtils.getBean(EvaluationUnitController.class).initializeData();
        pageDialog.show();
    }

    public void onEditTestedUnit() {
        var pageDialog = PageDialog.builder()
                .withIcon("dialog-edit-icon")
                .withTitle("被测单位")
                .withShowActions(false)
                .withContent(SpringUtils.getBean(AddTestedUnitView.class).getView())
                .build();
        SpringUtils.getBean(AddTestedUnitController.class).initializeData(this.currentProject.getTestedUnitId());
        pageDialog.show();
    }

    public void onDelProject() {
        AlertDialog alertDialog = AlertDialog.builder()
                .withMode(AlertMode.CONFIRM)
                .withType(AlertType.WARN)
                .withTitle("删除提示")
                .withContent("确认删除当前项目信息吗？")
                .build();
        alertDialog.show((confirm) -> {
            if (!confirm) {
                return;
            }
            deleteProject();
        });
    }

    public void reloadSystemList(){
        loadSystemData(0);
    }

    @FXML
    public void onAddSystem() {
        if(Objects.isNull(this.currentProject)){
            return;
        }
        //切换到添加系统页面
        GuiStore.setMainBodyContent(SpringUtils.getBean(AddSystemView.class).getView());
        SpringUtils.getBean(AddSystemController.class).initializeData(this.currentProject.getId());
    }

    private ContextMenu generateProjectListContextMenu() {
        final ContextMenu contextMenu = new ContextMenu();
        final MenuItem detailItem = new MenuItem("项目信息", IconUtils.getGlyph("edit-icon", "context-menu-icon"));
        detailItem.setOnAction(e -> this.onEditProject());
        final MenuItem evalUnitItem = new MenuItem("测评单位", IconUtils.getGlyph("eva-unit-icon", "context-menu-icon"));
        evalUnitItem.setOnAction(e -> onEditUnitItem());
        final MenuItem testedUnitItem = new MenuItem("被测单位", IconUtils.getGlyph("eva-unit-icon", "context-menu-icon"));
        testedUnitItem.setOnAction(e -> this.onEditTestedUnit());
        final MenuItem delItem = new MenuItem("删除项目", IconUtils.getGlyph("del-icon", "context-menu-icon"));
        delItem.setOnAction(e -> this.onDelProject());
        contextMenu.getItems().addAll(detailItem, evalUnitItem, testedUnitItem, delItem);
        return contextMenu;
    }

    private ContextMenu generateSystemListContextMenu(){
        final ContextMenu contextMenu = new ContextMenu();
        final MenuItem detailItem = new MenuItem("系统信息", IconUtils.getGlyph("edit-icon", "context-menu-icon"));
        detailItem.setOnAction(e -> this.onEditButtonClick());
        final MenuItem importItem = new MenuItem("导入数据", IconUtils.getGlyph("download-icon", "context-menu-icon"));
        importItem.setOnAction(e-> this.onImportSystem());
        final MenuItem exportItem = new MenuItem("导出数据", IconUtils.getGlyph("upload-icon", "context-menu-icon"));
        exportItem.setOnAction(e-> this.onExportSystem());
        final MenuItem delItem = new MenuItem("删除系统", IconUtils.getGlyph("del-icon", "context-menu-icon"));
        delItem.setOnAction(e -> this.onDelButtonClick());
        contextMenu.getItems().addAll(detailItem,importItem,exportItem, delItem);
        return contextMenu;
    }

    private void onImportSystem(){
        if (Objects.isNull(dataModel.getSelectedItem())) {
            SystemUtils.showMessageDialog("请选择要导入数据的系统！",MessageType.FAIL,this.page);
            return;
        }
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("选择导入的文档");
        var filter = Stream.of(CryptoType.values())
                .map(ct->new FileChooser.ExtensionFilter(ct.getName(), String.format("*%s",ct.getExtension())))
                .toList();
        fileChooser.getExtensionFilters().addAll(filter);
        File selectFile = fileChooser.showOpenDialog(GuiStore.getStage());
        if (Objects.isNull(selectFile)) {
            return;
        }
        try{
            this.enterSystemImport(dataModel.getSelectedItem(),new FileInputStream(selectFile));
        }catch(IOException ex){
            log.error("导入失败",ex);
        }

    }

    private void enterSystemImport(SystemDTO selectedSystem,InputStream fileStream) {
        SpinnerDialog spinnerDialog = SpinnerDialog.builder()
                .withProcessTip("正在读取导入数据内容...")
                .withContainer(GuiStore.getRoot())
                .build();
        spinnerDialog.show();
        CompletableFuture.supplyAsync(() -> this.systemImportService.loadImportSystemData(fileStream))
                .thenAccept(res -> Platform.runLater(() -> {
                    spinnerDialog.close();
                    if (res.isSuccess()) {
                        PageDialog pageDialog = PageDialog.builder()
                                .withIcon("upload-icon")
                                .withTitle(String.format("%s——数据导入", res.getData().getName()))
                                .withShowActions(false)
                                .withShowClose(false)
                                .withContent(SpringUtils.getBean(SystemImportView.class).getView())
                                .build();
                        SpringUtils.getBean(SystemImportController.class).initData(pageDialog, selectedSystem,this::reloadSystemList);
                        pageDialog.show();
                    } else {
                        MessageDialog.builder()
                                .withType(MessageType.FAIL).withMessage(res.getMsg())
                                .build().show();
                    }
                }));
    }


    private void onExportSystem() {
        if (Objects.isNull(dataModel.getSelectedItem())) {
            SystemUtils.showMessageDialog("请选择要导出数据的系统！",MessageType.FAIL,this.page);
            return;
        }
        File storageDir = FileUtils.getFile(this.sysConfigService.getExportDataStoragePath());
        if(!storageDir.exists()){
            SystemUtils.showMessageDialog("导出数据存储路径不存在，请设置默认存储路径！", MessageType.WARN, this.page);
            return;
        }
        SystemDTO systemDTO = dataModel.getSelectedItem();
        File filePath = FileUtils.getFile(this.sysConfigService.getExportDataStoragePath(),
                String.format("%s(%s)%s",systemDTO.getName(), ConvertUtils.toString(LocalDate.now()), CryptoType.ZIP_FILE.getExtension()));
        SpinnerDialog spinnerDialog = SpinnerDialog.builder()
                .withProcessTip("正在导出系统数据...")
                .build();
        systemExportService.setProcessCallback((value, tip) -> Platform.runLater(() -> {
            spinnerDialog.getSpinner().setProgress(value);
            spinnerDialog.setProcessTip(tip);
        }));
        spinnerDialog.show();
        CompletableFuture.supplyAsync(() -> this.systemExportService.exportSystemData(systemDTO.getId(),filePath))
                .thenAccept(res -> Platform.runLater(() -> {
                    spinnerDialog.close();
                    if (res.isSuccess()) {
                        SystemUtils.showMessageDialog("导出成功",MessageType.SUCCESS,this.page);
                        SystemUtils.openFile(filePath.getPath());
                    } else {
                        SystemUtils.showMessageDialog(res.getMsg(),MessageType.FAIL,this.page);
                    }
                }));
    }

    private void deleteProject(){
        CompletableFuture.supplyAsync(()->projectService.delete(this.currentProject.getId()))
                .thenAccept(res->Platform.runLater(()->{
                    if(res.isSuccess()){
                        //更新表数据
                        this.loadProjectList();
                        MessageDialog.builder()
                                .withType(MessageType.SUCCESS).withMessage("删除成功")
                                .build().show();
                    } else {
                        MessageDialog.builder()
                                .withType(MessageType.FAIL).withMessage(res.getMsg())
                                .build().show();
                    }
                }));
    }

    private void initMenuControls(){
        projectListView.setContextMenu(this.generateProjectListContextMenu());
        projectListView.setOnContextMenuRequested(eve->{
            var contextMenu = projectListView.getContextMenu();
            if(Objects.nonNull(contextMenu)){
                contextMenu.getItems().forEach(mi->mi.setDisable(Objects.isNull(currentProject)));
            }
        });
        projectListView.setCellFactory(cf->new MenuListCell(0,false));
        projectListView.getSelectionModel().selectedItemProperty()
                .addListener((observable, oldValue, newValue) -> {
                    if(Objects.nonNull(newValue)){
                        loadTaskList(newValue);
                    }
                });
    }

    private void initSystemTable(){
        tableView.setContextMenu(this.generateSystemListContextMenu());
        tableView.setOnContextMenuRequested(eve -> {
            SystemDTO selectedSystem = dataModel.getSelectedItem();
            var contextMenu = tableView.getContextMenu();
            if (Objects.nonNull(contextMenu)) {
                contextMenu.getItems().forEach(mi -> mi.setDisable(Objects.isNull(selectedSystem)));
            }
        });
        numberColumn.setCellFactory(col -> new NumberTableCell<>(this.pagination));
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
        systemLevelColumn.setCellValueFactory(new PropertyValueFactory<>("securityLevel"));
        systemOperateColumn.setCellFactory(col -> new SingleOperateTableCell<>(tableView, "系统测评", "evaluation-button-icon", this::onOptButtonClick));
        tableView.setItems(new FilteredList<>(dataModel.bodyProperty(), p -> true));
        dataModel.selectedIndexProperty().bind(tableView.getSelectionModel().selectedIndexProperty());
        pagination.pageCountProperty().bind(dataModel.pageCountProperty());
        pagination.currentPageIndexProperty()
                .addListener((obj, oldValue, number) -> {
                    if (Objects.nonNull(number)) {
                        loadSystemData(number.intValue());
                    }
                });
    }


    public String getResult() {
        return "";
    }

    private void loadProjectList(){
        LogUtils.info(log, "获取项目信息列表...");
        var filter = this.projectQueryText.getText();
        CompletableFuture.supplyAsync(()->projectService.findAll(filter))
                .thenAccept(res -> Platform.runLater(() -> this.initProjectList(res)))
                .exceptionally((ex) -> {
                    log.error("获取系统资产关联单元指标信息出错!", ex);
                    return null;
                });
    }

    private void loadSystemData(Integer page) {
        if(Objects.isNull(currentProject)){
            AppUtils.showMessageDialog("无对应的项目信息！", MessageType.FAIL,this.page);
            return;
        }
        CompletableFuture.supplyAsync(() -> this.systemService.findAllByProjectId(this.currentProject.getId(), PageRequest.of(page, TableDataModel.LIMIT)))
                .thenAccept(res -> Platform.runLater(() -> {
                    dataModel.bodyProperty().clear();
                    dataModel.setPageCount(res.getTotalPages());
                    dataModel.bodyProperty().addAll(res.getContent());
                }));
    }

    private void initProjectList(List<ProjectDTO> data){
        String selectCode = Objects.nonNull(projectListView.getSelectionModel().getSelectedItem()) ?
                projectListView.getSelectionModel().getSelectedItem().getCode() : StringUtils.EMPTY;
        projectListView.getItems().clear();
        this.projectList = data;
        if(CollectionUtils.isEmpty(data)){
            this.taskPane.setVisible(false);
            this.emptyPane.setVisible(true);
            this.currentProject = null;
            this.emptyDescLbl.setText(StringUtils.isNotBlank(projectQueryText.getText()) ? EMPTY_SEARCH_PROJECT_DESC:EMPTY_PROJECT_DESC);
        }else{
            this.emptyPane.setVisible(false);
            this.taskPane.setVisible(true);
            this.projectList.forEach(ute -> {
                var projectName = ute.getName();
                if(StringUtils.isNotBlank(ute.getNumber())){
                    projectName = String.format(PROJECT_NAME_FORMAT,ute.getNumber(),ute.getName());
                }
                projectListView.getItems().add(new MenuVO(2,ute.getId(),projectName,"project-icon","",ute.getName()));
            });
            var defaultItem = projectListView.getItems()
                    .stream()
                    .filter(mv-> StringUtils.isNotEmpty(selectCode) && StringUtils.equals(mv.getCode(),selectCode))
                    .findFirst();
            if(defaultItem.isPresent()){
                this.projectListView.getSelectionModel().select(defaultItem.get());
            }else{
                this.projectListView.getSelectionModel().select(0);
            }
        }
    }

    private void loadTaskList(@NonNull MenuVO projectVo){
        var project = this.projectList.stream()
                .filter(p-> StringUtils.equals(p.getId(),projectVo.getCode()))
                .findFirst()
                .orElse(null);
        if(Objects.isNull(project)){
            AppUtils.showMessageDialog("未找到对应的项目信息！", MessageType.FAIL,this.page);
            return;
        }
        currentProject = project;
        pagination.currentPageIndexProperty().setValue(0);
        loadSystemData(0);
    }

    private void onOptButtonClick(MouseEvent event){
        SystemDTO selectedSystem = dataModel.getSelectedItem();
        GuiStore.enterSystemEvaluation(CurrentSystemVO.builder()
                .projectId(selectedSystem.getProjectId())
                .projectName(selectedSystem.getProjectName())
                .systemId(selectedSystem.getId())
                .systemName(selectedSystem.getName())
                .securityLevel(SecurityLevel.fromName(selectedSystem.getSecurityLevel()))
                .build());
    }

    private void onEditButtonClick(){
        SystemDTO selectedSystem = dataModel.getSelectedItem();
        //切换到编辑项目页面
        var pageDialog = PageDialog.builder()
                .withIcon("dialog-edit-icon")
                .withTitle("系统信息")
                .withShowActions(false)
                .withContent(SpringUtils.getBean(AddSystemView.class).getView())
                .build();
        SpringUtils.getBean(AddSystemController.class).initializeData(selectedSystem);
        pageDialog.show();
    }

    private void onDelButtonClick(){
        SystemDTO selectedSystem = dataModel.getSelectedItem();
        AlertDialog alertDialog = AlertDialog.builder()
                .withMode(AlertMode.CONFIRM)
                .withType(AlertType.WARN)
                .withTitle("删除提示")
                .withContent("确认删除当前系统信息吗？")
                .build();
        alertDialog.show((confirm) -> {
            if (!confirm) {
                return;
            }
            checkDeleteSystem(selectedSystem.getId());
        });
    }

    private void checkDeleteSystem(final String systemId) {
        LogUtils.info(log, "执行系统删除前检查...");
        LogUtils.record(log, "请求数据：{}", systemId);
        CompletableFuture.supplyAsync(() -> this.systemService.checkEvalRecord(systemId))
                .thenAccept(res -> Platform.runLater(() -> {
                    if (res.isSuccess()) {
                        deleteSystem(systemId);
                    } else {
                        AlertDialog alertDialog = AlertDialog.builder()
                                .withMode(AlertMode.CONFIRM)
                                .withType(AlertType.WARN)
                                .withTitle("操作提示")
                                .withContent(res.getMsg())
                                .build();
                        alertDialog.show((confirm) -> {
                            if (confirm) {
                                deleteSystem(systemId);
                            }
                        });
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("执行系统删除前检查出错!", ex);
                    return null;
                });
    }

    private void deleteSystem(final String systemId) {
        SpinnerDialog spinnerDialog = SpinnerDialog.builder()
                .withProcessTip("正在删除系统数据...")
                .build();
        spinnerDialog.show();
        CompletableFuture.supplyAsync(() -> systemService.delete(systemId))
                .thenAccept(res -> Platform.runLater(() -> {
                    spinnerDialog.close();
                    if (res.isSuccess()) {
                        //更新表数据
                        this.loadSystemData(0);
                        AppUtils.showMessageDialog("系统删除成功",MessageType.SUCCESS,this.page);
                    } else {
                        AppUtils.showMessageDialog(res.getMsg(),MessageType.FAIL,this.page);
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("删除系统数据出错!", ex);
                    return null;
                });
    }

}